﻿using AiMoWindows.MainActions.Mode;
using System.Drawing;
using System.IO;

namespace AiMoWindows.CodeScripts.IO
{
    static class CodeIO
    {
        public const string Extension = ".AiMoScript";
        public static bool IsValidAiMoScriptFilePath(string maybeAFile)
        {
            return validExtensions.Contains(Path.GetExtension(maybeAFile).ToLower());
        }
        static readonly string[] validExtensions = new string[]
        {
            ".aimoscript",
            ".aimoscipts",
            ".sharekeydata",
        };
        internal static readonly string[] separator = new string[] { "\r\n", "\n" };

        public static void RepairFileExtension(ref string path)
        {
            string? directory = Path.GetDirectoryName(path);
            if (directory == null) return;
            RepairExtensions(directory, ref path);
        }
        public static void RepairExtensions(string folder)
        {
            string ss = "";
            RepairExtensions(folder, ref ss);
        }
        public static void RepairExtensions(string folder, ref string filePath)
        {
            try
            {
                DirectoryInfo directory = new DirectoryInfo(folder);
                if (directory.Exists)
                {
                    foreach (FileInfo file in directory.GetFiles())
                    {
                        try
                        {
                            if (file.Extension.Equals(".aimoscipts", StringComparison.CurrentCultureIgnoreCase) && !file.IsReadOnly)
                            {
                                string fullName = file.FullName.Replace(file.Extension, Extension);
                                file.MoveTo(fullName);
                            }
                        }
                        catch { }
                    }
                }
            }
            catch { }
            if (!string.IsNullOrEmpty(filePath))
            {
                string pathNew = filePath.Replace(Path.GetExtension(filePath), Extension);
                if (File.Exists(pathNew))
                {
                    filePath = pathNew;
                }
            }
        }
        /// <summary>
        /// 储存
        /// </summary>
        public static byte[] GetBytes(ScriptSource script, bool hasScreenShot)
        {
            Dictionary<string, string> dict = new Dictionary<string, string>
            {
                { "Guid", script.Attributes.Source },
                { "UserName", script.Attributes.UserName },
                { "CodeName", script.Attributes.CodeName.Trim() },
                { "Question", script.Attributes.Question.Trim() },
                { "RhinoVersion", script.Attributes.RhinoVersion },
                { "ScriptComplileMode", ((int)script.ScriptsMode).ToString() },
                { "Using", script.GetCompatibleIOUsings() },
                { "Author", CompatibleCommentStringForCodeIO(script.Attributes.Author) },
                { "Description", CompatibleCommentStringForCodeIO(script.Attributes.Description) },
                { "Main", script.MainCode.ToString() },
                { "Func", script.AdditionalFunctionCode.ToString() },
                { "Class", script.AdditionalClassCode.ToString() },
                { "ReferenceAssemblies", FromLines(script.ReferenceFiles) },
            };
            if (hasScreenShot)
                return CodeIOUtils.GetBytes(dict, script.Attributes.ScreenShot);
            else
                return CodeIOUtils.GetBytes(dict, null);
        }

        static bool DecodeString(ScriptSource script, string key, string value)
        {
            switch (key)
            {
                case "Guid":
                    script.Attributes.Source = value.Trim();
                    return true;
                case "UserName":
                    script.Attributes.UserName = value.Trim();
                    return true;
                case "CodeName":
                    script.Attributes.CodeName = value.Trim();
                    return true;
                case "Question":
                    script.Attributes.Question = value.Trim();
                    return true;
                case "RhinoVersion":
                    script.Attributes.RhinoVersion = value.Trim();
                    return true;
                case "Using":
                    script.SetUsingCode(value);
                    return true;
                case "Author":
                    script.Attributes.Author = value;
                    return true;
                case "Description":
                    script.Attributes.Description = value;
                    return true;
                case "Main":
                    script.MainCode.UpdateLines(value);
                    return true;
                case "Func":
                    script.AdditionalFunctionCode.UpdateLines(value);
                    return true;
                case "Class":
                    script.AdditionalClassCode.UpdateLines(value);
                    return true;
                case "ReferenceAssemblies":
                    script.AddReferenceFiles(ToLines(value));
                    return true;
                case "ScriptComplileMode":
                    if (int.TryParse(value, out int result))
                    {
                        script.UpdateScriptMode((ScriptComplileMode)result, out _);
                        return true;
                    }
                    return false;
                default:
                    return true;
            }
        }

        public static bool FromBytes(byte[] bytes, ScriptSource script, out string error)
        {
            Dictionary<string, string> paris = new Dictionary<string, string>();
            bool re = CodeIOUtils.FromBytes(bytes, paris, out Bitmap? bitmap, out error);
            if (re)
            {
                foreach (KeyValuePair<string, string> pair in paris)
                {
                    if (!DecodeString(script, pair.Key, pair.Value))
                    {
                        error = "Cannot decode content " + pair.Key;
                        return false;
                    }
                }
            }
            if (bitmap != null)
            {
                script.Attributes.ScreenShot = bitmap;
            }
            return true;
        }

        const string LineSpliter = "[%EFDS481J@#$BS]";
        public static string FromLines(string[] lines)
        {
            return string.Join(LineSpliter, lines);
        }

        public static string[] ToLines(string strings)
        {
            return strings.Split(LineSpliter, StringSplitOptions.RemoveEmptyEntries);
        }

        public static string CompatibleCommentStringForCodeIO(string text)
        {
            List<string> ss = new List<string>();
            foreach (string line in text.Split(separator, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
            {
                ss.Add($"// {line}");
            }
            return string.Join("\r\n", ss);
        }

        public static string CompatibleRemoveLineCommentSlash(string text)
        {
            List<string> ss = new List<string>();
            foreach (string line in text.Split(separator, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
            {
                if (line.StartsWith("//"))
                {
                    ss.Add(line[2..].Trim());
                }
                else ss.Add(line);
            }
            return string.Join("\r\n", ss);
        }
    }
}
